﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Globalization;

namespace PSE.Framework.Common
{
    [Serializable]
    public class Version : ICloneable, IComparable, IComparable<Version>, IEquatable<Version>
    {
        private int _major;
        private int _minor;
        private int _revision;
        private int _build;

        public int Build
        {
            get
            {
                return this._build;
            }
            set
            {
                this._build = value;
            }
        }

        public int Major
        {
            get
            {
                return this._major;
            }
            set
            {
                this._major = value;
            }
        }

        public int Minor
        {
            get
            {
                return this._minor;
            }
            set
            {
                this._minor = value;
            }
        }

        public int Revision
        {
            get
            {
                return this._revision;
            }
            set
            {
                this._revision = value;
            }
        }

        public short MajorRevision
        {
            get
            {
                return (short)(this._revision >> 0x10);
            }
            set
            {
                this._revision = (this._revision & 0x7fff) | Convert.ToInt32(value << 0x10);
            }
        }

        public short MinorRevision
        {
            get
            {
                return (short)(this._revision & 0x7fff);
            }
            set
            {
                this._revision = (this._revision & 0x7fff0000) | Convert.ToInt32(value);
            }
        }

        public Version()
        {
            this._major = 0;
            this._minor = 0;
            this._build = -1;
            this._revision = -1;
        }

        public Version(int major, int minor)
        {
            this._build = -1;
            this._revision = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "Version's parameters must be greater than or equal to zero.");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "Version's parameters must be greater than or equal to zero.");
            }
            this._major = major;
            this._minor = minor;
        }

        public Version(int major, int minor, int revision)
        {
            this._build = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "Version's parameters must be greater than or equal to zero.");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "Version's parameters must be greater than or equal to zero.");
            }
            if (revision < 0)
            {
                throw new ArgumentOutOfRangeException("revision", "Version's parameters must be greater than or equal to zero.");
            }
            this._major = major;
            this._minor = minor;
            this._revision = revision;
        }

        public Version(int major, int minor, int revision, int build)
        {
            this._build = -1;
            this._revision = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "Version's parameters must be greater than or equal to zero.");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "Version's parameters must be greater than or equal to zero.");
            }
            if (build < 0)
            {
                throw new ArgumentOutOfRangeException("build", "Version's parameters must be greater than or equal to zero.");
            }
            if (revision < 0)
            {
                throw new ArgumentOutOfRangeException("revision", "Version's parameters must be greater than or equal to zero.");
            }
            this._major = major;
            this._minor = minor;
            this._build = build;
            this._revision = revision;
        }

        public Version(string version)
        {
            if (string.IsNullOrEmpty(version))
                throw new ArgumentNullException("version");

            string[] parts = version.Split('.');
            if (parts == null || parts.Length != 4)
                throw new InvalidCastException("The informed version is invalid");

            int major, minor, build, revision;
            major = Convert.ToInt32(parts[0]);
            minor = Convert.ToInt32(parts[1]);
            revision = Convert.ToInt32(parts[2]);
            build = Convert.ToInt32(parts[3]);

            this._build = -1;
            this._revision = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "Version's parameters must be greater than or equal to zero.");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "Version's parameters must be greater than or equal to zero.");
            }
            if (build < 0)
            {
                throw new ArgumentOutOfRangeException("build", "Version's parameters must be greater than or equal to zero.");
            }
            if (revision < 0)
            {
                throw new ArgumentOutOfRangeException("revision", "Version's parameters must be greater than or equal to zero.");
            }
            this._major = major;
            this._minor = minor;
            this._build = build;
            this._revision = revision;
        }

        public object Clone()
        {
            Version version = new Version();
            version._major = this._major;
            version._minor = this._minor;
            version._build = this._build;
            version._revision = this._revision;
            return version;
        }

        public int CompareTo(object version)
        {
            if (version == null)
            {
                return 1;
            }
            Version version2 = version as Version;
            if (version2 == null)
            {
                throw new ArgumentException("Object must be of type Version.");
            }
            if (this._major != version2._major)
            {
                if (this._major > version2._major)
                {
                    return 1;
                }
                return -1;
            }
            if (this._minor != version2._minor)
            {
                if (this._minor > version2._minor)
                {
                    return 1;
                }
                return -1;
            }

            if (this._revision != version2._revision)
            {
                if (this._revision > version2._revision)
                {
                    return 1;
                }
                return -1;
            }

            if (this._build == version2._build)
            {
                return 0;
            }
            if (this._build > version2._build)
            {
                return 1;
            }

            return -1;
        }

        public int CompareTo(Version value)
        {
            if (value == null)
            {
                return 1;
            }
            if (this._major != value._major)
            {
                if (this._major > value._major)
                {
                    return 1;
                }
                return -1;
            }
            if (this._minor != value._minor)
            {
                if (this._minor > value._minor)
                {
                    return 1;
                }
                return -1;
            }
            if (this._revision != value._revision)
            {
                if (this._revision > value._revision)
                {
                    return 1;
                }
                return -1;
            }
            if (this._build == value._build)
            {
                return 0;
            }
            if (this._build > value._build)
            {
                return 1;
            }
            return -1;
        }

        public override bool Equals(object obj)
        {
            Version version = obj as Version;
            if (version == null)
            {
                return false;
            }

            return (((this._major == version._major) && (this._minor == version._minor)) && ((this._build == version._build) && (this._revision == version._revision)));
        }

        public bool Equals(Version obj)
        {
            if (obj == null)
            {
                return false;
            }

            return (((this._major == obj._major) && (this._minor == obj._minor)) && ((this._build == obj._build) && (this._revision == obj._revision)));
        }

        public override int GetHashCode()
        {
            int num = 0;
            num |= (this._major & 15) << 0x1c;
            num |= (this._minor & 0xff) << 20;
            num |= (this._build & 0xff) << 12;
            return (num | (this._revision & 0xfff));
        }

        public static bool operator ==(Version v1, Version v2)
        {
            if (object.ReferenceEquals(v1, null))
            {
                return object.ReferenceEquals(v2, null);
            }
            return v1.Equals(v2);
        }

        public static bool operator >(Version v1, Version v2)
        {
            return (v2 < v1);
        }

        public static bool operator >=(Version v1, Version v2)
        {
            return (v2 <= v1);
        }

        public static bool operator !=(Version v1, Version v2)
        {
            return !(v1 == v2);
        }

        public static bool operator <(Version v1, Version v2)
        {
            if (v1 == null)
            {
                throw new ArgumentNullException("v1");
            }
            return (v1.CompareTo(v2) < 0);
        }

        public static bool operator <=(Version v1, Version v2)
        {
            if (v1 == null)
            {
                throw new ArgumentNullException("v1");
            }
            return (v1.CompareTo(v2) <= 0);
        }

        public override string ToString()
        {
            if (this._revision == -1)
            {
                return this.ToString(2);
            }
            if (this._build == -1)
            {
                return this.ToString(3);
            }

            return this.ToString(4);
        }

        public string ToString(int fieldCount)
        {
            switch (fieldCount)
            {
                case 0:
                    return string.Empty;

                case 1:
                    return this._major.ToString();

                case 2:
                    return (this._major + "." + this._minor);
            }
            if (this._build == -1)
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Argument must be between {0} and {1}.", new object[] { "0", "2" }), "fieldCount");
            }
            if (fieldCount == 3)
            {
                return string.Concat(new object[] { this._major, ".", this._minor, ".", this._revision });
            }
            if (this._revision == -1)
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Argument must be between {0} and {1}.", new object[] { "0", "3" }), "fieldCount");
            }
            if (fieldCount != 4)
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Argument must be between {0} and {1}.", new object[] { "0", "4" }), "fieldCount");
            }

            return string.Concat(new object[] { this.Major, ".", this._minor, ".", this._revision, ".", this._build });
        }
    }
}